% ==== PCANet Demo =======
% T.-H. Chan, K. Jia, S. Gao, J. Lu, Z. Zeng, and Y. Ma, 
% "PCANet: A simple deep learning baseline for image classification?" 
% IEEE Trans. Image Processing, vol. 24, no. 12, pp. 5017-5032, Dec. 2015. 

% Tsung-Han Chan [chantsunghan@gmail.com]
% Please email me if you find bugs, or have suggestions or questions!
% ========================
% DEMO: Testing PCANet on FERET database
clear all; close all; clc; 
addpath('./Utils');
addpath('./Liblinear');
addpath('./mat');

%% Loading data from feret
load FERET_crop_database
TrnSize = size(fa_150X90,2); 
ImgSize = [150 90]; 

TrnData = fa_150X90;
TrnLabels = fa_label; 

TestData = dup2_150X90; %fb_150X90, fc_150X90, dup1_150X90, dup2_150X90
TestLabels = dup2_label; %fb_label, fc_label, dup1_label, dup2_label

ImgFormat = 'gray'; %'color';

% === load PCANet filters trained by MultiPIE database
load PCANet_2_filters_MultiPIE_FaceRecog
clear PCANet; % clear pcanet setting in MultiPIE

%% For this demo, we subsample the Training and Testing sets 
% plz comment out the following four lines for a complete test.
% when you want to do so, please ensure that your computer memory is more than 64GB. 
% training linear SVM classifier on large amount of high dimensional data would 
% requires lots of memory. 
%%%%%%%%%%%%%%%%%%%%%%%%
nTestImg = length(TestLabels);


%% PCANet parameters (they should be funed based on validation set; i.e., ValData & ValLabel)
PCANet.NumStages = 2;
PCANet.PatchSize = [5 5];
PCANet.NumFilters = [8 8];
PCANet.HistBlockSize = [15 15];
PCANet.BlkOverLapRatio = 0;
PCANet.Pyramid = [];

fprintf('\n ====== PCANet Parameters ======= \n')
PCANet

%% PCANet Training 
fprintf('\n ====== PCANet Training ======= \n')
%every cell is a image matrix
TrnData_ImgCell = mat2imgcell(double(TrnData),ImgSize(1),ImgSize(2),ImgFormat); % convert columns in TrnData to cells 
tic; 
%V:weight of the PCAnet
ftrain = PCANet_FeaExt(TrnData_ImgCell,V,PCANet);
%ftrain:f,index of block
PCANet_TrnTime = toc;

fprintf('\n ====== Performing WPCA ======= \n')
%To perform a fair comparison with prior methods,
%the dimensions of the PCANet features are reduced to 1000 via
%a whitening PCA
[U, D, ~] = fsvd(ftrain,1000,2,true); % fast but an approximation
%[U, D, ~] = svds(ftrain,1000); % more accurate, but takes much long time
U = U*diag(1./diag(D));
ftrain = U'*ftrain; %result of train data and weight


%% PCANet Feature Extraction and Testing 

TestData_ImgCell = mat2imgcell(TestData,ImgSize(1),ImgSize(2),ImgFormat); % convert columns in TestData to cells 
clear TestData; 

fprintf('\n ====== PCANet Testing ======= \n')

nCorrRecog = 0;
RecHistory = zeros(nTestImg,1);

tic; 
for idx = 1:1:nTestImg
    ftest = PCANet_FeaExt(TestData_ImgCell(idx),V,PCANet); % extract a test feature using trained PCANet model
    
    ftest = U'*ftest; 
    
    [~, ind] = min(dist_cosine(ftest,ftrain));%least distance index from train for the test image
    xLabel_est = TrnLabels(ind);
    
    if xLabel_est == TestLabels(idx)
        RecHistory(idx) = 1;
        nCorrRecog = nCorrRecog + 1;
    end
    
    if 0==mod(idx,5)%check every 5 images
        fprintf('Accuracy up to %d tests is %.2f%%; taking %.2f secs per testing sample on average. \n',...
            [idx 100*nCorrRecog/idx toc/idx]);%test images, accuray,average time 
    end 
    
    TestData_ImgCell{idx} = [];
    
end
Averaged_TimeperTest = toc/nTestImg;%average time
Accuracy = nCorrRecog/nTestImg; 
ErRate = 1 - Accuracy;

%% Results display
fprintf('\n ===== Results of PCANet, followed by a linear SVM classifier =====');
fprintf('\n     PCANet training time: %.2f secs.', PCANet_TrnTime);
fprintf('\n     Testing accuracy: %.2f%%', 100*Accuracy);
fprintf('\n     Average testing time %.2f secs per test sample. \n\n',Averaged_TimeperTest);










